//$Id: TileSuperClass.java 25 2010-10-21 10:44:10Z vincentngik $

package builders;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

/**
 * This is the ATD for the following classes:
 * TileManager
 * Tile
 * HomeBase
 * TileResult
 * 
 * @author Vincent
 */
public class TileATD {

	TileManager tileManager;
	File inputFile;
	File expectedOutputFile;
	Tile[] tiles;
	String tempExpectedOutput;
	int testCount;
	int errorCount;

	/**
	 * This is the constructor of the TileATD class.
	 * 
	 * @param inputFile				specify the file name of the file from which inputs are read
	 * @param expectedOutputFile	specify the file name of the file from which expected outputs are read
	 */
	TileATD(String inputFile, String expectedOutputFile) {
		this.tileManager = new TileManager("testDataForTile.txt");
		this.inputFile = new File(inputFile);
		this.expectedOutputFile = new File(expectedOutputFile);
		this.tiles = tileManager.getTiles();
	}

	/*
	 * this method will read each test cases from the file specified by inputFile and compare the results from the test cases against the expected outputs.
	 * the expected outputs are read from the file specified by expectedOutputFile.
	 * 
	 * the test will end when the line read from the input file is "end of test" .
	 * 
	 * this method will read the test line provided in the input file and match it to a method call that is from one of the four classes being tested.
	 */
	public void executeTestCases(){

		try {
			FileReader inputFileReader = new FileReader(inputFile);
			BufferedReader inputReader = new BufferedReader (inputFileReader);

			FileReader expectedOutputFileReader = new FileReader(expectedOutputFile);
			BufferedReader expectedOutputReader = new BufferedReader (expectedOutputFileReader);

			String line = inputReader.readLine();
			System.out.println(line);

			while (!(line = inputReader.readLine()).equals("end of test")){
				String testDescription = line;
				System.out.println(testDescription);

				String testLine = inputReader.readLine();

				int methodNumber = matchTestLineToMethodCall(testLine);

				switch (methodNumber) {
				case 1: 
					checkTileGetterMethods(inputReader, expectedOutputReader);
					break;
				case 2:
					checkTileResultGetterMethods(inputReader, expectedOutputReader);
					break;
				case 3:
					checkTiles(inputReader, expectedOutputReader);
					break;
				case 4:
					checkTileManagerSquareToTile(inputReader, expectedOutputReader);
					break;
				case 5:
					checkTileExecute(inputReader, expectedOutputReader);
					break;
				case 6:
					checkTileManagerMakeTiles(inputReader, expectedOutputReader);
					break;
				}

			}

			System.out.println(line);
			System.out.println("Total number of test cases: " + testCount);
			System.out.println("Total number of errors: " + errorCount);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * @param testLine	the test line that is read from the input file
	 * @return			an integer which will correspond to a method from one of the four classes being tested
	 */
	private int matchTestLineToMethodCall(String testLine) {
		int methodNumber = 0;

		if (testLine.equals("check getter methods for Tile")){
			methodNumber = 1;
		} else if (testLine.equals("check getter methods for TileResult")){
			methodNumber = 2;
		} else if (testLine.equals("print all tiles")){
			methodNumber = 3;
		} else if (testLine.equals("TileManager squareToTile(int)")){
			methodNumber = 4;
		} else if (testLine.equals("Tile execute(int)")){
			methodNumber = 5;
		} else if (testLine.equals("TileManager makeTiles(String)")){
			methodNumber = 6;
		}

		return methodNumber;
	}

	/**
	 * @param errorMessage	the error message that is read from the input file
	 * @param expected		the expected result that is read from the expected output file
	 * @param result		the result from the testing of the method
	 */
	private void printErrorMessage(String errorMessage,
			String expected, String result) {
		System.out.println(errorMessage);
		System.out.println("expected: " + expected);
		System.out.println("result: " + result);
		errorCount++;
	}

	/** 
	 * @param errorMessage		the error message this is to be printed if the test case failed
	 * @param expectedResult	the expected result that is supposed to be produce in the test case
	 * @param result			the result from doing the test case
	 */
	private void doTestCase(String errorMessage, String expectedResult, String result) {
		if (!expectedResult.equals(result)){
			printErrorMessage(errorMessage, expectedResult, result);
		}
		testCount++;
	}

	/*
	 * the format used to create the following methods which are used for testing:
	 * 1) the error messages from the input file are read
	 * 2) any input data needed for testing are read from the input file
	 * 3) if necessary, those input data that need to be convert to other types are done
	 * 3.1) if necessary, creation of new objects are done
	 * 4) the expected result are read from the expected output file
	 * 4.1) for each expected result, it is compared to the result from calling the method that is being tested
	 * 5) the 'end of test' message is read from expected output file to indicate that the test case is done 
	 */

	/*
	 * this test case will check all the getter methods in Tile and HomeBase.
	 * 
	 * The tested getter methods:
	 * -getMultiplier
	 * -getResource
	 * -getTileDescription
	 * -getNameOfTile
	 * -getImageFile
	 * -getIsHomeBase
	 */
	private void checkTileGetterMethods(BufferedReader inputReader,
			BufferedReader expectedOutputReader) {

		try {
			String errorMultiplier = inputReader.readLine();
			String errorResource = inputReader.readLine();
			String errorTileDescription = inputReader.readLine();
			String errorNameOfTile = inputReader.readLine();
			String errorImageFile = inputReader.readLine();
			String errorIsHomeBase = inputReader.readLine();

			String multiplier = inputReader.readLine(); 
			String resource = inputReader.readLine();
			String tileDescription = inputReader.readLine();
			String nameOfTile = inputReader.readLine();
			String imageFile = inputReader.readLine();

			Tile newTile = new Tile(multiplier, resource, tileDescription, nameOfTile, imageFile);

			// for tile
			doTestCase(errorMultiplier, expectedOutputReader.readLine(), Integer.toString(newTile.getMultiplier()));
			doTestCase(errorResource, expectedOutputReader.readLine(), newTile.getResource().toString());
			doTestCase(errorTileDescription, expectedOutputReader.readLine(), newTile.getTileDescription());
			doTestCase(errorNameOfTile, expectedOutputReader.readLine(), newTile.getNameOfTile());
			doTestCase(errorImageFile, expectedOutputReader.readLine(), newTile.getImageFile());
			doTestCase(errorIsHomeBase, expectedOutputReader.readLine(), new Boolean(newTile.getIsHomeBase()).toString());

			// for homeBase
			String homeBaseTileDescription = inputReader.readLine();
			String homeBaseNameOfTile = inputReader.readLine();
			String homeBaseImageFile = inputReader.readLine();

			HomeBase newHomeBase = new HomeBase(homeBaseTileDescription, homeBaseNameOfTile, homeBaseImageFile);

			String homeBase = " for homeBase";

			doTestCase(errorTileDescription + homeBase, expectedOutputReader.readLine(), newHomeBase.getTileDescription());
			doTestCase(errorNameOfTile + homeBase, expectedOutputReader.readLine(), newHomeBase.getNameOfTile());
			doTestCase(errorImageFile + homeBase, expectedOutputReader.readLine(), newHomeBase.getImageFile());
			doTestCase(errorIsHomeBase + homeBase, expectedOutputReader.readLine(), new Boolean(newHomeBase.getIsHomeBase()).toString());

			// end of test
			expectedOutputReader.readLine();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/*
	 * this test case will check all the getter methods in TileResult.
	 * 
	 * The tested getter methods:
	 * -getResourceThatIsAffected
	 * -getAmountToIncrease
	 * -getTileDescription
	 * -getIsHomeBase
	 */
	private void checkTileResultGetterMethods(BufferedReader inputReader,
			BufferedReader expectedOutputReader) {

		try {
			String errorResource = inputReader.readLine();
			String errorAmount = inputReader.readLine();
			String errorTileDescription = inputReader.readLine();
			String errorIsHomeBase = inputReader.readLine();

			String stringResource = inputReader.readLine();
			String stringAmount = inputReader.readLine(); 
			String tileDescription = inputReader.readLine();

			int amount = Integer.valueOf(stringAmount);
			Player.Resource resource = searchResource(stringResource);

			TileResult newTileResult = new TileResult(resource, amount, tileDescription);

			// for tile
			doTestCase(errorResource, expectedOutputReader.readLine(), newTileResult.getResourceThatIsAffected().toString());
			doTestCase(errorAmount, expectedOutputReader.readLine(), Integer.toString(newTileResult.getAmountToIncrease()));
			doTestCase(errorTileDescription, expectedOutputReader.readLine(), newTileResult.getTileDescription());
			doTestCase(errorIsHomeBase, expectedOutputReader.readLine(), new Boolean(newTileResult.getIsHomeBase()).toString());

			// for homeBase
			String homeBaseTileDescription = inputReader.readLine();

			TileResult newTileResultForHomeBase = new TileResult(homeBaseTileDescription);

			String homeBase = " for homeBase";

			doTestCase(errorTileDescription + homeBase, expectedOutputReader.readLine(), newTileResultForHomeBase.getTileDescription());
			doTestCase(errorIsHomeBase + homeBase, expectedOutputReader.readLine(), new Boolean(newTileResultForHomeBase.getIsHomeBase()).toString());

			// end of test
			expectedOutputReader.readLine();

		}catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * @param stringResource	the string that is read from the input file
	 * @return					the Player.Resource that corresponds to the string specified in the parameter
	 */
	private Player.Resource searchResource(String stringResource) {

		Player.Resource requestedResource = null;

		for (Player.Resource resource : Player.Resource.values()){
			if (resource.toString().equals(stringResource)){
				requestedResource = resource;
			}
		}

		return requestedResource;
	}

	/*
	 * this test case will check that all the data from the input file are loaded correctly by the TileManager.
	 */
	private void checkTiles(BufferedReader inputReader, BufferedReader expectedOutputReader) {

		try {
			String errorMultiplier = inputReader.readLine();
			String errorResource = inputReader.readLine();
			String errorTileDescription = inputReader.readLine();
			String errorNameOfTile = inputReader.readLine();
			String errorImageFile = inputReader.readLine();

			for (int i = 0; i < TileManager.TOTAL_NUMBER_OF_TILES_IN_BOARD; i++){

				// for Tile
				if (!tiles[i].getIsHomeBase()){
					doTestCase(errorMultiplier + i, expectedOutputReader.readLine(), Integer.toString(tiles[i].getMultiplier()));
					doTestCase(errorResource + i, expectedOutputReader.readLine(), tiles[i].getResource().toString());
				} 

				// common among Tile and HomeBase
				doTestCase(errorTileDescription + i, expectedOutputReader.readLine(), tiles[i].getTileDescription());
				doTestCase(errorNameOfTile + i, expectedOutputReader.readLine(), tiles[i].getNameOfTile());
				doTestCase(errorImageFile + i, expectedOutputReader.readLine(), tiles[i].getImageFile());
			}

			// end of test
			expectedOutputReader.readLine();

		}catch (IOException e) {
			e.printStackTrace();
		}
	}

	/*
	 * This test case will check the method squareToTile from TileManager.
	 *
	 * the method getNameOfTile is used to verify that the method squareToTile is working correctly.
	 */
	private void checkTileManagerSquareToTile(BufferedReader inputReader,
			BufferedReader expectedOutputReader) {

		try {
			String errorMessage = inputReader.readLine();

			String stringTileNumber = inputReader.readLine();
			String stringHomeBaseNumber = inputReader.readLine();

			int tileNumber = Integer.valueOf(stringTileNumber);
			int homeBaseNumber = Integer.valueOf(stringHomeBaseNumber);

			doTestCase(errorMessage + tileNumber, expectedOutputReader.readLine(), tileManager.squareToTile(tileNumber).getNameOfTile());
			doTestCase(errorMessage + homeBaseNumber, expectedOutputReader.readLine(), tileManager.squareToTile(homeBaseNumber).getNameOfTile());

			// end of test
			expectedOutputReader.readLine();

		} catch (IOException e) {
			e.printStackTrace();
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} 
	}

	/*
	 * this test case will check the method execute from Tile.
	 * 
	 * the method getTileDescription is used to verify that the method execute is working correctly.
	 */
	private void checkTileExecute(BufferedReader inputReader,
			BufferedReader expectedOutputReader) {

		try {
			String errorMessage = inputReader.readLine();

			String stringTileNumber = inputReader.readLine();
			String stringHomeBaseNumber = inputReader.readLine();
			String stringNumberOfActionPoints = inputReader.readLine();

			int tileNumber = Integer.valueOf(stringTileNumber);
			int homeBaseNumber = Integer.valueOf(stringHomeBaseNumber);
			int numberOfActionPoints = Integer.valueOf(stringNumberOfActionPoints);

			doTestCase(errorMessage + tileNumber + ": using input " + numberOfActionPoints, tempExpectedOutput = expectedOutputReader.readLine(), tiles[tileNumber].execute(numberOfActionPoints).getTileDescription());

			/*
			 *  call the same method on the same tile twice
			 *  - should get the same result
			 */
			doTestCase(errorMessage + tileNumber + ": using input " + numberOfActionPoints, tempExpectedOutput, tiles[tileNumber].execute(numberOfActionPoints).getTileDescription());

			doTestCase(errorMessage + homeBaseNumber + ": using input " + numberOfActionPoints, expectedOutputReader.readLine(), tiles[homeBaseNumber].execute(numberOfActionPoints).getTileDescription());

			// end of test
			expectedOutputReader.readLine();

		} catch (IOException e) {
			e.printStackTrace();
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} 
	}

	/*
	 * this test case will check the methods makeTiles and getTilesData from TileManager.
	 */
	private void checkTileManagerMakeTiles(BufferedReader inputReader,
			BufferedReader expectedOutputReader) {

		try{
			String errorMessage = inputReader.readLine();
			
			tileManager.makeTiles(tileManager.getTilesData());
			tiles = tileManager.getTiles();

			for (int i = 0; i < TileManager.TOTAL_NUMBER_OF_TILES_IN_BOARD; i++){

				// for Tile
				if (!tiles[i].getIsHomeBase()){
					doTestCase(errorMessage, expectedOutputReader.readLine(), Integer.toString(tiles[i].getMultiplier()));
					doTestCase(errorMessage, expectedOutputReader.readLine(), tiles[i].getResource().toString());
				} 

				// common among Tile and HomeBase
				doTestCase(errorMessage, expectedOutputReader.readLine(), tiles[i].getTileDescription());
				doTestCase(errorMessage, expectedOutputReader.readLine(), tiles[i].getNameOfTile());
				doTestCase(errorMessage, expectedOutputReader.readLine(), tiles[i].getImageFile());
			}

			// end of test
			expectedOutputReader.readLine();

		}catch (IOException e) {
			e.printStackTrace();
		}
	}

	/*
	 * the input file name: testcasesForTile.txt
	 * the expected output file name: expectedResultsForTile.txt
	 * the data used for Tile Manager: testDataForTile.txt
	 */
	public static void main(String[] args) throws Exception {
		String workingDir = System.getProperty("user.dir");
		String inputFile = workingDir + "\\src\\builders\\testcasesForTile.txt";
		String expectedOutputFile = workingDir + "\\src\\builders\\expectedResultsForTile.txt";
		TileATD atd = new TileATD(inputFile, expectedOutputFile);
		atd.executeTestCases();
	}
}